<?php
/**
 * Copyright (c) 2020
 * 摘    要：
 * 作    者：san
 * 修改日期：2020.04.11
 */

namespace App\Service;

use App\Model\Environment;
use App\Model\Project;
use App\Model\Role;
use App\Model\Task;
use App\Model\User;
use App\Model\WorkSpace;
use App\Model\WorkSpaceUser;
use ErrorException;
use Exception;
use App\Library\AutoDs\Repo;
use Hyperf\Database\Model\Builder;
use Hyperf\Database\Model\Collection;
use Hyperf\Database\Model\Model;
use Hyperf\Utils\Context;

class TaskService extends BaseService
{
    /**
     * TaskService constructor.
     */
    public function __construct()
    {
        parent::__construct();

        $this->redis = redis();
    }

    /**
     * @param $page
     * @param $pageSize
     * @param $condition
     * @return array
     */
    public function query($page, $pageSize, $condition)
    {
        $fields = [
            'task.id',
            'task.user_id',
            'task.project_id',
            'task.workspace_id',
            'task.action',
            'task.status',
            'task.title',
            'task.branch',
            'task.commit_id',
            'task.link_id',
            'task.ex_link_id',
            'task.file_transmission_mode',
            'task.enable_rollback',
            'task.gray_push',
            'task.auditor',
            'task.created_at',
            'task.updated_at',
            'u.nick_name as user_name',
            'p.name as project_name',
            'e.name as env_name',
            'p.audit',
        ];
        $uid    = Context::get('user')->user_id;

        $query = Task::query(true)
            ->select($fields)
            ->leftJoin('users as u', 'task.user_id', '=', 'u.user_id')
            ->leftJoin('project as p', 'task.project_id', '=', 'p.id')
            ->leftJoin('environment as e', 'p.level', '=', 'e.id')
            ->whereNull('u.deleted_at')
            ->whereNull('p.deleted_at')
            ->whereNull('e.deleted_at')
            ->whereNull('task.deleted_at');

        if ($uid != User::ADMIN_UID) {
            //查询负责部门下的上线单
            $exRecord = WorkSpaceUser::query(true)
                ->where(function ($query) {
                    $query->where('role_id', Role::ROLE_OWNER)
                        ->orWhere('role_id', Role::ROLE_MASTER);
                })->where('user_id', $uid)
                ->get('workspace_id');

            $workspaceIds = [];

            if ($exRecord) {
                array_map(function ($item) use (&$workspaceIds) {
                    $workspaceIds[] = $item['workspace_id'];
                }, $exRecord->toArray());
            }

            $query = $query->where('task.user_id', $uid);

            if ($workspaceIds) {
                $query = $query->orWhereIn('task.workspace_id', $workspaceIds);
            }
        }

        if ($condition) {
            $query = $query->where($condition);
        }

        $count  = $query->count();
        $result = $query->limit($pageSize)->offset(($page - 1) * $pageSize)
            ->orderBy('task.id', 'desc')
            ->get();

        foreach ($result as $key => $value) {
            $hasPermission = WorkSpaceUser::query(true)
                ->where(function ($query) {
                    $query->where('role_id', Role::ROLE_OWNER)
                        ->orWhere('role_id', Role::ROLE_MASTER);
                })
                ->where('user_id', $uid)
                ->where('workspace_id', $value['workspace_id'])
                ->get()->toArray();

            if ($uid === User::ADMIN_UID) {
                $result[$key]['showAudit'] = 1;
            } else {
                $result[$key]['showAudit'] = $hasPermission ? 1 : 0;
            }
        }

        return [
            'list'  => $result,
            'total' => $count,
        ];
    }

    /**
     * 新增上线单
     *
     * @param $data
     * @throws ErrorException
     * @throws Exception
     * @return bool
     */
    public function add($data)
    {
        $project_id = $data['project_id'];
        $project    = $this->_checkProjectExits($project_id);

        $grayPush = $data['gray_push'];
        $this->_checkCanGrayPush($project->is_open_gray, $grayPush);

        $saveData = [
            'project_id'             => $project_id,
            'workspace_id'           => $project->workspace_id,
            'title'                  => $data['title'],
            'branch'                 => $data['branch'],
            'commit_id'              => $data['commit_id'],
            'file_transmission_mode' => $data['file_transmission_mode'],
            'gray_push'              => $grayPush,
            'user_id'                => Context::get('user')->user_id,
            'status'                 => $project->audit == Project::AUDIT_YES ? Task::STATUS_SUBMIT : Task::STATUS_PASS,
        ];

        $res = Task::query()->insert($saveData);
        if (!$res) {
            throw new ErrorException(t('message.12002'));
        }

        return true;
    }

    /**
     * @param $id
     * @throws ErrorException
     * @return array
     */
    public function show($id)
    {
        $detail = self::_checkExits($id);

        $detail      = $detail->toArray();
        $workspaceId = $detail['get_project']['workspace_id'];
        $envId       = $detail['get_project']['level'];

        $env       = Environment::query(true)->find($envId);
        $workspace = WorkSpace::query(true)->find($workspaceId);

        return [
            'detail'         => $detail,
            'project_name'   => $detail['get_project']['name'],
            'env_name'       => $env->name,
            'workspace_name' => $workspace->name,
        ];
    }

    /**
     * @param $id
     * @throws ErrorException
     * @throws Exception
     * @return bool
     */
    public function delete($id)
    {
        $detail = self::_checkExits($id);

        $res = $detail->delete();

        if (!$res) {
            throw new ErrorException("操作失败");
        }

        return true;
    }

    /**
     * 检测 Task 是否存在 (此处不能修改成private类型)
     *
     * @param $id
     * @throws ErrorException
     * @return Builder|Builder[]|Collection|Model|null
     */
    public static function _checkExits($id)
    {
        $detail = Task::query(true)->with('getProject')->find($id);
        if (!$detail) {
            throw new ErrorException(t('message.12031'));
        }

        return $detail;
    }

    /**
     * 检测 Project 是否存在
     *
     * @param $id
     * @throws ErrorException
     * @return Builder|Builder[]|Collection|Model|null
     */
    private function _checkProjectExits($id)
    {
        $detail = Project::query(true)->find($id);

        if (!$detail) throw new ErrorException("message.12039");

        return $detail;
    }

    /**
     * 检测是否可以灰度发布
     *
     * @param $isOpenGrayPush
     * @param $grayPush
     * @throws Exception
     * @return bool
     */
    private function _checkCanGrayPush($isOpenGrayPush, $grayPush)
    {
        if ($isOpenGrayPush == Project::NONE_OPEN_GRAY && $grayPush == Project::IS_OPEN_GRAY) {
            throw new \Exception(t('message.12048'));
        }

        return true;
    }

    /**
     * 获取分支列表
     *
     * @param $projectId
     * @throws ErrorException
     * @throws Exception
     * @return array
     */
    public function branchList($projectId)
    {
        $project = Project::getInstance($projectId);
        $version = Repo::getInstance($project);

        return $version->getBranchList();
    }

    /**
     * 获取tag列表
     *
     * @param $projectId
     * @throws ErrorException
     * @throws Exception
     * @return array
     */
    public function tagList($projectId)
    {
        $project = Project::getInstance($projectId);
        $version = Repo::getInstance($project);

        return $version->getTagList();
    }


    /**
     * 获取提交历史
     *
     * @param $projectId
     * @param $branch
     * @throws ErrorException
     * @throws Exception
     * @return array
     */
    public function commitList($projectId, $branch)
    {
        $project = Project::getInstance($projectId);
        $version = Repo::getInstance($project);

        if ($project->repo_mode == Project::REPO_MODE_TAG) {
            $list = $version->getTagList();
        } else {
            $list = $version->getCommitList($branch);
        }

        return $list;
    }

    /**
     * 上线单审核
     *
     * @param $taskId
     * @throws ErrorException
     * @return bool
     */
    public function audit($taskId)
    {
        $uuid = Context::get('user')->user_id;

        $task = self::_checkExits($taskId);
        $this->_checkHasAuditPermission($task, $uuid);

        if (!in_array($task->status, [Task::STATUS_SUBMIT, Task::STATUS_PASS])) {
            throw new ErrorException(t('message.12026'));
        }

        $task->auditor = $uuid;
        $task->status  = ($task->status == Task::STATUS_SUBMIT) ? Task::STATUS_PASS : Task::STATUS_SUBMIT;
        $res           = $task->save();

        if (!$res) {
            throw new ErrorException(t('message.12002'));
        }

        return true;
    }

    /**
     * 检测是否有审核的权限
     *
     * @param Task $task
     * @param $uuid
     * @throws ErrorException
     * @return bool
     */
    private function _checkHasAuditPermission(Task $task, $uuid)
    {
        $workspaceId = $task->getProject->workspace_id;
        //不是管理员
        if ($uuid != User::ADMIN_UID) {
            $hasExits = WorkSpaceUser::query(true)
                ->where('workspace_id', $workspaceId)
                ->where('user_id', $uuid)
                ->whereIn('role_id', [Role::ROLE_OWNER, Role::ROLE_MASTER])
                ->get();

            if (!$hasExits) {
                throw new ErrorException(t('message.12040'));
            }
        }

        return true;
    }

    /**
     * 生成回滚
     *
     * @param $taskId
     * @throws ErrorException
     * @return bool
     */
    public function rollback($taskId)
    {
        $uuid = Context::get('user')->user_id;
        // task检测
        $task = self::_checkExits($taskId);

        // project检测
        $project = $this->_checkProjectExits($task->project_id);

        // 只有自己的上线单 才可以生成回滚单
        if ($task->user_id != $uuid) {
            throw new ErrorException(t('message.12028'));
        }

        //第一次部署
        if ($task->ex_link_id == '') {
            throw new ErrorException(t('message.12025'));
        }

        // 不能重复回滚
        if ($task->ex_link_id == $task->link_id) {
            throw new ErrorException(t('message.12021'));
        }

        // 第一次部署
        $hasRollback = Task::query(true)
            ->where([
                'project_id' => $task->project_id,
                'status'     => $task->status,
                'gray_push'  => $task->gray_push,
            ])
            ->where('id', '<>', $task->id)
            ->first();

        if (!$hasRollback) {
            throw new ErrorException(t('message.12025'));
        }

        // 6、新增一条记录 hyperf好像不支持 $attributes 赋值
        $saveData = [
            'user_id'                => $task->user_id,
            'project_id'             => $task->project_id,
            'ex_link_id'             => $task->ex_link,
            'branch'                 => $task->branch,
            'file_transmission_mode' => $task->file_transmission_mode,
            'file_list'              => $task->file_list,
            'enable_rollback'        => $task->enable_rollback,
            'gray_push'              => $task->gray_push,
            'commit_id'              => $task->getRollbackCommitId(),
            'status'                 => $project->audit == Project::AUDIT_YES ? Task::STATUS_SUBMIT : Task::STATUS_PASS,
            'action'                 => Task::ACTION_ROLLBACK,
            'link_id'                => $task->ex_link_id,
            'title'                  => $task->title . ' - ' . '回滚',
            'auditor'                => $project->audit == Project::AUDIT_YES ? 0 : $task->auditor,
        ];

        $res = Task::query()->insert($saveData);

        if (!$res) {
            throw new ErrorException(t('message.12002'));
        }

        return true;
    }
}
